function UploaderFile (uploader, file, status) {
	// Parse the info
	if(file) {
		var fileSize = 0;
	    if (file.size > 1024 * 1024)
	      fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
	    else
	      fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
	      
	    this.info = {
	    	name:file.name,
	    	size:fileSize,
	    	type:file.type
	    }; 
	   }
    
    this.file = file;
	this.uploader = uploader;
	
	
	if(!status) {
		status = 'pending';
	}
	this.status = status;
	
	var newDate = new Date;
    this.uid = newDate.getTime();
    
	this.li = $('<li/>');
};

UploaderFile.prototype.add = function(data) {
	var file = this;
	var removeHandler = this.uploader.params.removeHandler;
	if(this.status == 'uploaded') {
		var pos = data.location.indexOf('/');
		var fileName = data.location.substr(pos + 1);
		if(this.uploader.progressDisplay == 'bar') {
			var html = fileName + '<div class="uploader-progress-container"><div class="uploader-progress-bar"></div></div>';
		}
		else {
			var html = fileName + '<div class="uploader-progress-display"></div>';
		}
		html = '<img class="uploader-progress uploader-progress-success" src="' + this.uploader.directory + 'images/uploader-check.png"/>' + html;
		// Run the "on complete" handler
		var runComplete = true;
	}
	else {
		var xhr = new XMLHttpRequest();
		if(xhr.upload) {
			if(this.uploader.progressDisplay == 'bar') {
				var html = this.info.name + ' (' + this.info.size + ')' + '<div class="uploader-progress-container"><div class="uploader-progress-bar"></div></div>';
			}
			else {
				var html = this.info.name + ' (' + this.info.size + ')' + '<div class="uploader-progress-display"></div>';
			}
		}
		else {
			if(this.uploader.progressDisplay == 'bar') {
				var html = this.info.name + ' (' + this.info.size + ')' + '<div class="uploader-progress-container"></div>';
			}
			else {
				var html = this.info.name + ' (' + this.info.size + ')' + '<div class="uploader-progress-display"></div>';
			}
		}
		if(this.status == 'error') {
			html = '<img class="uploader-progress" src="' + this.uploader.directory + 'images/uploader-x.png"/>' + html;
		}
		else {
			html = '<img class="uploader-progress" src="' + this.uploader.directory + 'images/uploader-arrow.png"/>' + html;
		}
	
	}
	
	this.li.appendTo(this.uploader.ui.fileList).html(html);
	$('<button/>').html('X').appendTo(this.li).data('id', this.uid).data('file', file).data('uploader', this.uploader).click(function(e) {
		e.preventDefault();
		var button = this;
		$(this).parent('li').fadeOut(100, function() {
			
			if($(button).data('uploader').params.onFileRemove) {
				$(button).data('skycraper').params.onFileRemove($(button).data('uploader').files[$(button).data('id')]);
			}
			// Run delete handler if the file is uploaded
			if(file.status == 'uploaded') {
				$.ajax({
					url:$(button).data('uploader').params.removeHandler,
					data:$(button).data('file').serverResponse,
					dataType:'json',
					success:function(data) {
						if($(button).data('uploader').params.onDelete) {
							$(button).data('uploader').params.onDelete(data, $(button).data('file'));
						}
					},
					error:function(xhr, error, exception) {
						if($(button).data('uploader').params.onDeleteFailed) {
							$(button).data('uploader').params.onDeleteFailed(error, exception, $(button).data('file'));
						}
					}
				});
			}
			
			delete $(button).data('uploader').files[$(button).data('id')];
			$(button).data('uploader').checkChooseButton();
		});
	});
	
	if(this.uploader.params.onFileAdd) {
		this.uploader.params.onFileAdd(this.uploader, this);
	}
	
	this.uploader.files[this.uid] = this;
	this.uploader.checkChooseButton();
	if(runComplete) this.complete(data, true);

};

UploaderFile.prototype.progress = function(evt) {
	if(this.uploader.onProgress) {
		this.uploader.onProgress(this.uploader, this, evt);
	}
	
  	if (evt.lengthComputable) {
  		if(this.uploader.progressDisplay == 'bar') {
	    	var percentComplete = Math.round(evt.loaded * 220 / evt.total);
	    	this.li.find('.uploader-progress-bar').html(Math.round(evt.loaded * 100 / evt.total).toString() + '%');
	    	this.li.find('.uploader-progress-bar').css('width', percentComplete.toString() + 'px');
	    }
	    else {
	    	this.li.find('.uploader-progress-display').html(Math.round(evt.loaded * 100 / evt.total).toString() + '%');
	    }
  	}
  	else {
  	}
  	
};

UploaderFile.prototype.complete = function(data, hideMessage) {
	if(this.uploader.params.onUploadCompleted) {
		this.uploader.params.onUploadCompleted(data, this);
	}
	this.serverResponse = data;
  	this.li.children('img.uploader-progress').attr('src', this.uploader.directory + 'images/uploader-check.png');
  	if(this.uploader.progressDisplay == 'bar') {
  		
	  	if(this.li.find('.uploader-progress-bar').length) {
	 		this.li.find('.uploader-progress-bar').css('width', '220px').html('100%');
	 	}
	 	else {
	 		this.li.find('.uploader-progress-container').children('img').remove();
	 		this.li.find('.uploader-progress-container').html('Complete!');
	 	}
	}
	else {
		this.li.find('.uploader-progress-display').html('Complete!');
	}
 	
 	this.status = 'uploaded';
 	this.uploader.uploadInfo.numberCompleted++;
 	
 	var count = this.uploader.pendingCount;
 	
 	
 	if(this.uploader.uploadInfo.numberCompleted == count && !hideMessage) {
 		this.uploader.showMessage('success', 'File(s) uploaded successfully.');
 		this.uploader.checkChooseButton();
 	}
 	this.uploader.checkUploadButton();
	
};

UploaderFile.prototype.failed = function (error, exception) {
	if(this.uploader.onUploadFailed) {
		this.uploader.onUploadFailed(error, exception, this);
	}
	if(this.li.find('.uploader-progress-bar').length) {
 		this.li.find('.uploader-progress-bar').css('width', '20px');
 	}
 	else {
 		this.li.find('.uploader-progress-container').children('img').remove();
 		this.li.find('.uploader-progress-container').html('Error.');
 	}
	this.li.children('img').attr('src', this.uploader.directory + 'images/uploader-x.png');
	this.status = 'error';
};


UploaderFile.prototype.upload = function() {
	if(this.uploader.preUpload) {
		this.uploader.preUpload(this.uploader, this);
	}
	var self = this;
	
  	var xhr = new XMLHttpRequest();
  	var fd = new FormData();
  	fd.append('files', this.file);
  	/* event listners */
  	if(xhr.upload) {
	  	xhr.upload.addEventListener("progress", function(event) {
	  		self.progress(event);
	  	}, false);
	}
	else {
		this.li.find('.uploader-progress-container').append('<img src="' + this.uploader.directory + 'images/uploader-loader.gif"/>');
	}
  	xhr.addEventListener("load", function(event) {
  		var response = $.parseJSON(this.responseText);
  		self.complete(response);
  	}, false);
  	xhr.addEventListener("error", this.failed, false);
  	xhr.addEventListener("abort", this.canceled, false);
  	xhr.open("POST", this.uploader.params.uploadHandler, true);  
    xhr.setRequestHeader("X_FILENAME", this.file.name);  
    xhr.send(this.file);
  	
};

UploaderFile.prototype.remove = function() {
	var file = this;
	// Remove the file from the queue if it's there
	if(this.li) {
		this.li.fadeOut(100, function() {
			$(this).remove();
		});
	}
	
	// Remove the file from the array of files
	
};

function Uploader (element, params) {
	this.element = $(element);
	
	if(!params) params = {};
	
	if(params.maxFiles && params.minFiles && params.maxFiles < params.minFiles) {
		alert("Warning: you can't have a maximum file count less than your minimum file count.");
		return false;
	}
	
	this.params = $.extend({
			uploadHandler:'uploader-handler.php',
			removeHandler:'uploader-remove.php',
			maxFiles:false,
			minFiles:false,
			validTypes:false
		},
		params);
};



Uploader.prototype.checkChooseButton = function() {
	// If they reached the max number of files, make the "choose file" button disabled
	if(this.params.maxFiles && Object.keys(this.files).length >= (this.params.maxFiles)) {
		
		this.ui.chooseFile.attr('disabled', 'disabled');
	}
	
	else {
		this.ui.chooseFile.attr('disabled', false);
	}
};

Uploader.prototype.checkUploadButton = function() {
	// Check if there are any "pending" files in the queue, if so, activate the button.
	// if not, disable it
	var activate = false;
	
	for(var i in this.files) {
		if(this.files[i].status == 'pending') {
			activate = true;
			break;
		}
	}
	
	// Then, check max and min
	var length = Object.keys(this.files).length;
	if(this.params.maxFiles && length > this.params.maxFiles) {
		activate = false;
	}
	
	if(this.params.minFiles && length < this.params.minFiles) {
		activate = false;
	}
	
	if(activate) {
		this.ui.uploadFiles.attr('disabled', false);
	}
	else {
		this.ui.uploadFiles.attr('disabled', 'disabled');
	}
};

Uploader.prototype.showMessage = function(type, message) {
	var self = this;
	if(!type) type = 'message';
	this.ui.message.data('uploader', self).html('<p class="' + type + '">' + message + '</p>');
	$('<button/>').html('X').data('uploader', self).appendTo(this.ui.message.children('p')).click(function(e) {
		e.preventDefault();
		var self = $(this).data('uploader');
		self.ui.message.slideUp(100);
	});
	clearTimeout(this.ui.messageTimeout);
	this.ui.message.slideDown(100, function() {
		var self = $(this).data('uploader');
		self.ui.messageTimeout = setTimeout(function(){self.ui.message.slideUp(100);}, 5000);
	});
};

Uploader.prototype.initialize = function() {
	this.directory = concerto.includesUrl + 'concerto/app-dev-suite/uploader/'
	
	var self = this;
	
	this.ui = {};
	this.files = {};
	this.uploadInfo = {
		numberCompleted:0,
		errors:0
	};
	
	this.ui.container = $('<div/>').addClass('uploader').insertAfter(this.element);
	this.ui.fileList = $('<ul/>').addClass('uploader-file-list').appendTo(this.ui.container);
	
	
	this.ui.message = $('<div/>').addClass('uploader-message').appendTo(this.ui.container);
	// Hide the file upload interface from the browser and create our own that links to it
	// We can't actually hide it, otherwise the button is unclickable for some reason,
	// so we set its height and width to 0
	this.element.css('height', '0px').css('width', '0px').data('uploader', this).attr('multiple', 'multiple').change(function() {
		
		var files = $(this).get(0).files;
		if($(this).data('uploader').params.maxFiles && (files.length + Object.keys($(this).data('uploader').files).length) > $(this).data('uploader').params.maxFiles) {
			$(this).data('uploader').showMessage('error', 'You have exceeded the file limit <b>(' + $(this).data('uploader').params.maxFiles + ')</b>.');
		}
		else {
			for(var i=0;i<files.length;i++) {
				// Check the file type
				var type = files[i].type;
				if($(this).data('uploader').params.validTypes && $(this).data('uploader').params.validTypes.indexOf(type) < 0) {
					$(this).data('uploader').showMessage('error', 'Invalid file type: <b>' + type + '</b>.');
				}
				else {
					console.log(files[i]);
					var file = new UploaderFile($(this).data('uploader'), files[i]);
					file.add();
					
				}
			}
			$(this).data('uploader').checkUploadButton();
		}
		
		$(this).get(0).value = '';
	});
	this.ui.chooseFile = $('<button/>').data('uploader', self).html('Select File(s)').appendTo(this.ui.container).click(function(e) {
		e.preventDefault();
		$(this).data('uploader').element.trigger('click');
	});
	
	this.ui.uploadFiles = $('<button/>').data('uploader', this).html('Upload File(s)').appendTo(this.ui.container).click(function(e) {
		e.preventDefault();
		$(this).data('uploader').pendingCount = 0;
		$(this).attr('disabled', 'disabled');
		$(this).data('uploader').ui.chooseFile.attr('disabled', 'disabled');
		$(this).data('uploader').uploadInfo.numberCompleted = 0;
		$(this).data('uploader').uploadInfo.errors = 0;
		for(var i in $(this).data('uploader').files) {
			if($(this).data('uploader').files[i].status == 'pending' || !$(this).data('uploader').files[i].status) {
				$(this).data('uploader').pendingCount++;
				$(this).data('uploader').files[i].upload();
			}
		}
	});
	
	if(this.params.size == 'small') {
		this.progressDisplay = 'text';
	}
	else {
		this.progressDisplay = 'bar';
	}
	
	this.checkUploadButton();
	
	if(this.params.onInit) {
		this.params.onInit(this);
	}
	
};

(function($){
	$.fn.uploader = function(params, value) {
		return this.each(function() {
			if(typeof(params) == 'string') {
				switch(params) {
					case 'add_file':
						var file = new UploaderFile($(this).data('uploader'), false, 'uploaded');
						value.status = 'success';
						file.add(value);
						break;
				}
			}
			else {
				var self = new Uploader(this, params);
				self.initialize();
			}
		});


	};
})(jQuery);